package com.vonchenchen.renderer;

import android.app.Service;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static android.graphics.ImageFormat.YUV_420_888;

/**
 * Created by  on 2021/1/27.
 */
public class VideoCaptureCamera2 extends VideoCapture {
    private static final boolean DEBUG = false;
    private CameraDevice mCameraDevice = null;
    private CaptureRequest.Builder mPreviewBuilder = null;
    private CameraCaptureSession mCaptureSession = null;
    private ImageReader mImageReader = null;
    private static final double kNanoSecondsToFps = 1.0E-9D;
    private static final String TAG = "CAMERA2";
    private VideoCaptureCamera2.CameraState mCameraState;
    private CameraManager mManager;
    private Handler mMainHandler;
    private HandlerThread mPreviewThread;
    private final Object mCameraStateLock;
    private int mExpectedFrameSize;
    private int mCaptureWidth;
    private int mCaptureHeight;
    private int mCaptureFps;
    private int mCaptureFormat;
    private byte[] mCaptureData;
    private boolean mFaceDetectSupported;
    private int mFaceDetectMode;
    private boolean mIsAutoFaceFocusEnabled;
    private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{new MeteringRectangle(0, 0, 0, 0, 0)};
    private MeteringRectangle[] mAFAERegions;
    private static final float ZOOM_UNSUPPORTED_DEFAULT_VALUE = 1.0F;
    private static final float DEFAULT_VALUE = -1.0F;
    private float mLastZoomRatio;
    private float mCurZoomRatio;
    private float mMaxZoom;
    private Rect mSensorRect;
    public CameraManager.AvailabilityCallback mAvailabilityCallback;
    private final CameraCaptureSession.CaptureCallback mCaptureCallback;
    private CameraCaptureSession.CaptureCallback mAfCaptureCallback;

    private static CameraCharacteristics getCameraCharacteristics(Context appContext, int id) {
        CameraManager manager = (CameraManager)appContext.getSystemService(Service.CAMERA_SERVICE);

        try {
            return manager.getCameraCharacteristics(Integer.toString(id));
        } catch (CameraAccessException var4) {
            Log.i("CAMERA2", "GetNumberOfCameras: getCameraIdList(): " + var4);
        } catch (Exception var5) {
            Log.i("CAMERA2", "GetNumberOfCameras: got exception: " + var5);
        }

        return null;
    }

    static boolean isLegacyDevice(Context appContext, int id) {
        try {
            CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(appContext, id);
            return cameraCharacteristics != null && (Integer)cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 2;
        } catch (Throwable var3) {
            Log.w("CAMERA2", "this is a legacy camera device");
            return true;
        }
    }

    static int getNumberOfCameras(Context appContext) {
        CameraManager manager = (CameraManager)appContext.getSystemService(Service.CAMERA_SERVICE);

        try {
            return manager.getCameraIdList().length;
        } catch (Exception var3) {
            Log.e("CAMERA2", "GetNumberOfCameras: getCameraIdList(): ", var3);
            return 0;
        }
    }

    static String getName(int id, Context appContext) {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(appContext, id);
        if (cameraCharacteristics == null) {
            return null;
        } else {
            int facing = (Integer)cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
            return "camera2 " + id + ", facing " + (facing == 0 ? "front" : "back");
        }
    }

    static String getCaptureName() {
        return "camera2";
    }

    static int getSensorOrientation(int id, Context appContext) {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(appContext, id);
        return cameraCharacteristics == null ? -1 : (Integer)cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    }

    VideoCaptureCamera2(Context context, int id, long nativeVideoCaptureDeviceAndroid) {
        super(context, id, nativeVideoCaptureDeviceAndroid);
        this.mCameraState = VideoCaptureCamera2.CameraState.STOPPED;
        this.mManager = null;
        this.mMainHandler = new Handler(this.mContext.getMainLooper());
        this.mPreviewThread = null;
        this.mCameraStateLock = new Object();
        this.mExpectedFrameSize = 0;
        this.mCaptureWidth = -1;
        this.mCaptureHeight = -1;
        this.mCaptureFps = -1;
        //this.mCaptureFormat = 35;
        this.mCaptureFormat = YUV_420_888;
        this.mIsAutoFaceFocusEnabled = false;
        this.mAFAERegions = ZERO_WEIGHT_3A_REGION;
        this.mLastZoomRatio = -1.0F;
        this.mCurZoomRatio = 1.0F;
        this.mMaxZoom = -1.0F;
        this.mSensorRect = null;
        this.mAvailabilityCallback = new CameraManager.AvailabilityCallback() {
            public synchronized void onCameraAvailable(String cameraId) {
                super.onCameraAvailable(cameraId);
                if (VideoCaptureCamera2.this.mCameraState == VideoCaptureCamera2.CameraState.EVICTED && VideoCaptureCamera2.this.tryOpenCamera() != 0) {
                    Log.e("CAMERA2", "start capture failed");
                }

            }

            public synchronized void onCameraUnavailable(String cameraId) {
                super.onCameraUnavailable(cameraId);
                Log.e("CAMERA2", "Camera " + cameraId + " unavailable");
            }
        };
        this.mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            private long mLastFocusedTs;

            private void notifyCameraFocusAreaChanged(Rect cropRegion, Rect faceRect) {
                Rect rect = CoordinatesTransform.sensorToNormalizedPreview(faceRect, VideoCaptureCamera2.this.mCaptureWidth, VideoCaptureCamera2.this.mCaptureHeight, cropRegion);
                Log.d("CAMERA2", "face bound = " + faceRect.toString());
                Log.d("CAMERA2", "rect (-1000, 1000) = " + rect.toString());
                boolean isMirror = false;
                if (VideoCaptureCamera2.this.mId == 1) {
                    isMirror = true;
                }

                RectF rectF = CoordinatesTransform.normalizedFaceRect(rect, 0, isMirror);
                Log.d("CAMERA2", "preview size width = " + VideoCaptureCamera2.this.mCaptureWidth + " height = " + VideoCaptureCamera2.this.mCaptureHeight);
                Log.d("CAMERA2", "auto face focus left =" + rectF.left + " top = " + rectF.top + " right = " + rectF.right + " bottom = " + rectF.bottom + "isMirror =" + isMirror);
                float x = rectF.left;
                float y = rectF.top;
                float width = rectF.width();
                float height = rectF.height();
                if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                    VideoCaptureCamera2.this.NotifyCameraFocusAreaChanged(x, y, width, height, VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid);
                }

            }

            private void process(CaptureResult result) {
                Face[] faces = (Face[])result.get(CaptureResult.STATISTICS_FACES);
                if (faces != null && faces.length > 0) {
                    Rect cropRegion;
                    if (System.currentTimeMillis() - this.mLastFocusedTs < 3000L) {
                        if (faces[0].getScore() > 20) {
                            cropRegion = (Rect)result.get(CaptureResult.SCALER_CROP_REGION);
                            this.notifyCameraFocusAreaChanged(cropRegion, faces[0].getBounds());
                        }

                        return;
                    }

                    if (faces[0].getScore() <= 50) {
                        return;
                    }

                    VideoCaptureCamera2.this.mAFAERegions = new MeteringRectangle[]{new MeteringRectangle(faces[0].getBounds(), 1000)};
                    VideoCaptureCamera2.this.addRegionsToCaptureRequestBuilder(VideoCaptureCamera2.this.mPreviewBuilder);
                    if (VideoCaptureCamera2.this.mCameraState != VideoCaptureCamera2.CameraState.STARTED) {
                        return;
                    }

                    try {
                        cropRegion = (Rect)result.get(CaptureResult.SCALER_CROP_REGION);
                        Log.d("CAMERA2", "cropRegion = " + cropRegion.toString());
                        Log.d("CAMERA2", "capture size wxh = " + VideoCaptureCamera2.this.mCaptureWidth + " x " + VideoCaptureCamera2.this.mCaptureHeight);
                        this.notifyCameraFocusAreaChanged(cropRegion, faces[0].getBounds());
                        VideoCaptureCamera2.this.mCaptureSession.capture(VideoCaptureCamera2.this.mPreviewBuilder.build(), VideoCaptureCamera2.this.mCaptureCallback, (Handler)null);
                    } catch (Exception var4) {
                        Log.e("CAMERA2", "capture: " + var4);
                        return;
                    }

                    VideoCaptureCamera2.this.createCaptureRequest();
                    this.mLastFocusedTs = System.currentTimeMillis();
                } else {
                    VideoCaptureCamera2.this.mAFAERegions = VideoCaptureCamera2.ZERO_WEIGHT_3A_REGION;
                }

            }

            public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
            }

            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                if (VideoCaptureCamera2.this.mIsAutoFaceFocusEnabled && VideoCaptureCamera2.this.isAutoFaceFocusSupported()) {
                    this.process(result);
                }

            }
        };
        this.mAfCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            private void process(CaptureResult result) {
                Integer afState = (Integer)result.get(CaptureResult.CONTROL_AF_STATE);
                if (null != afState) {
                    if (4 == afState || 5 == afState) {
                        VideoCaptureCamera2.this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 2);
                        VideoCaptureCamera2.this.startNormalPreview();
                    }

                }
            }

            public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
                this.process(partialResult);
            }

            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                this.process(result);
            }
        };
    }

    private void startNormalPreview() {
        this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, 3);
        this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, 1);
        Handler backgroundHandler = new Handler(this.mPreviewThread.getLooper());

        try {
            this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), this.mCaptureCallback, backgroundHandler);
        } catch (CameraAccessException var3) {
            Log.e("CAMERA2", "setRepeatingRequest failed, error message : " + var3.getMessage());
        }

    }

    private void changeCameraStateAndNotify(VideoCaptureCamera2.CameraState state) {
        synchronized(this.mCameraStateLock) {
            this.mCameraState = state;
            this.mCameraStateLock.notifyAll();
        }
    }

    private int createCaptureRequest() {
        try {
            this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), this.mCaptureCallback, (Handler)null);
            return 0;
        } catch (CameraAccessException var2) {
            Log.e("CAMERA2", "setRepeatingRequest: ", var2);
            return -1;
        } catch (IllegalArgumentException var3) {
            Log.e("CAMERA2", "setRepeatingRequest: ", var3);
            return -2;
        } catch (SecurityException var4) {
            Log.e("CAMERA2", "setRepeatingRequest: ", var4);
            return -3;
        } catch (IllegalStateException var5) {
            Log.e("CAMERA2", "capture:" + var5);
            return -4;
        }
    }

    private static void readImageIntoBuffer(Image image, byte[] data) {
        int width = image.getWidth();
        int height = image.getHeight();
        Image.Plane[] planes = image.getPlanes();
        int offset = 0;

        for(int plane = 0; plane < planes.length; ++plane) {
            ByteBuffer buffer = planes[plane].getBuffer();
            if (buffer == null) {
                Log.e("CAMERA2", "plane " + plane + " buffer is null ");
                break;
            }

            int rowStride = planes[plane].getRowStride();
            int pixelStride = planes[plane].getPixelStride();
            int planeWidth = plane == 0 ? width : width / 2;
            int planeHeight = plane == 0 ? height : height / 2;
            if (pixelStride == 1 && rowStride == planeWidth) {
                buffer.get(data, offset, planeWidth * planeHeight);
                offset += planeWidth * planeHeight;
            } else {
                byte[] rowData = new byte[rowStride];

                int col;
                for(col = 0; col < planeHeight - 1; ++col) {
                    buffer.get(rowData, 0, rowStride);

                    for(col = 0; col < planeWidth; ++col) {
                        data[offset++] = rowData[col * pixelStride];
                    }
                }

                buffer.get(rowData, 0, Math.min(rowStride, buffer.remaining()));

                for(col = 0; col < planeWidth; ++col) {
                    data[offset++] = rowData[col * pixelStride];
                }
            }
        }

    }

    private int tryOpenCamera() {
        VideoCaptureCamera2.CrStateListener stateListener = new VideoCaptureCamera2.CrStateListener();

        try {
            this.mManager.openCamera(Integer.toString(this.mId), stateListener, this.mMainHandler);
            return 0;
        } catch (CameraAccessException var3) {
            Log.e("CAMERA2", "allocate: manager.openCamera: ", var3);
            return -1;
        } catch (IllegalArgumentException var4) {
            Log.e("CAMERA2", "allocate: manager.openCamera: ", var4);
            return -2;
        } catch (SecurityException var5) {
            Log.e("CAMERA2", "allocate: manager.openCamera: ", var5);
            return -3;
        } catch (Exception var6) {
            Log.e("CAMERA2", "unknown error", var6);
            return -4;
        }
    }

    private void addRegionsToCaptureRequestBuilder(CaptureRequest.Builder builder) {
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, 2);
        builder.set(CaptureRequest.CONTROL_AE_REGIONS, this.mAFAERegions);
        builder.set(CaptureRequest.CONTROL_AF_REGIONS, this.mAFAERegions);
        builder.set(CaptureRequest.CONTROL_AF_MODE, 1);
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, 0);
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, 1);
    }

    private int doStartCapture() {
        boolean maxImages = true;
        int bufSize = this.mCaptureWidth * this.mCaptureHeight * ImageFormat.getBitsPerPixel(this.mCaptureFormat) / 8;
        this.mExpectedFrameSize = bufSize;
        this.mCaptureData = new byte[this.mExpectedFrameSize];
        this.mImageReader = ImageReader.newInstance(this.mCaptureWidth, this.mCaptureHeight, this.mCaptureFormat, 2);
        if (this.mPreviewThread == null) {
            this.mPreviewThread = new HandlerThread("CameraPreview");
            this.mPreviewThread.start();
        }

        Handler backgroundHandler = new Handler(this.mPreviewThread.getLooper());
        VideoCaptureCamera2.ImageReaderListener imageReaderListener = new VideoCaptureCamera2.ImageReaderListener();
        this.mImageReader.setOnImageAvailableListener(imageReaderListener, backgroundHandler);

        try {
            this.mPreviewBuilder = this.mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        } catch (CameraAccessException var11) {
            Log.e("CAMERA2", "createCaptureRequest: ", var11);
            return -1;
        } catch (IllegalArgumentException var12) {
            Log.e("CAMERA2", "createCaptureRequest: ", var12);
            return -2;
        } catch (SecurityException var13) {
            Log.e("CAMERA2", "createCaptureRequest ", var13);
            return -3;
        }

        if (this.mPreviewBuilder == null) {
            Log.e("CAMERA2", "mPreviewBuilder error");
            return -4;
        } else {
            this.mPreviewBuilder.addTarget(this.mImageReader.getSurface());
            this.mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, 1);
            this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, 3);
            this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, 1);
            this.setFaceDetect(this.mPreviewBuilder, this.mFaceDetectMode);
            List<Surface> surfaceList = new ArrayList(1);
            surfaceList.add(this.mImageReader.getSurface());
            VideoCaptureCamera2.CaptureSessionListener captureSessionListener = new VideoCaptureCamera2.CaptureSessionListener();

            try {
                this.mCameraDevice.createCaptureSession(surfaceList, captureSessionListener, (Handler)null);
                return 0;
            } catch (CameraAccessException var8) {
                Log.e("CAMERA2", "createCaptureSession :", var8);
                return -1;
            } catch (IllegalArgumentException var9) {
                Log.e("CAMERA2", "createCaptureSession :", var9);
                return -2;
            } catch (SecurityException var10) {
                Log.e("CAMERA2", "createCaptureSession :", var10);
                return -3;
            }
        }
    }

    private int doStopCapture() {
        if (this.mPreviewThread != null) {
            this.mPreviewThread.quitSafely();
            this.mPreviewThread = null;
        }

        if (this.mCaptureSession != null) {
            try {
                this.mCaptureSession.abortCaptures();
                this.mCaptureSession = null;
            } catch (CameraAccessException var2) {
                Log.e("CAMERA2", "abortCaptures: ", var2);
                return -1;
            } catch (IllegalStateException var3) {
                Log.e("CAMERA2", "abortCaptures: ", var3);
                return -1;
            }
        }

        if (this.mImageReader != null) {
            this.mImageReader.setOnImageAvailableListener(null, null);
            this.mImageReader.close();
            this.mImageReader = null;
        }

        if (this.mCameraDevice != null) {
            this.mCameraDevice.close();
            this.mCameraDevice = null;
        }

        return 0;
    }

    public int UnRegisterNativeHandle() {
        this.mNativeVideoCaptureDeviceAndroid = 0L;
        return 0;
    }

    public int allocate() {
        synchronized(this.mCameraStateLock) {
            if (this.mCameraState == VideoCaptureCamera2.CameraState.OPENING) {
                Log.e("CAMERA2", "allocate() invoked while Camera is busy opening/configuring");
                return -1;
            }
        }

        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            return -1;
        } else {
            if (VideoCapture.fetchCapability(this.mId, this.mContext, getCaptureName()) == null) {
                createCapabilities(this.mId, this.mContext);
            }

            if (this.mNativeVideoCaptureDeviceAndroid != 0L) {
                this.mIsAutoFaceFocusEnabled = this.isAutoFaceFocusEnabled(this.mNativeVideoCaptureDeviceAndroid);
            }

            this.mCameraNativeOrientation = (Integer)cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
            this.mManager = (CameraManager)this.mContext.getSystemService(Service.CAMERA_SERVICE);
            int[] availableFDModes = (int[])cameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
            int maxFDCount = (Integer)cameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT);
            if (availableFDModes.length > 1 && maxFDCount > 0) {
                this.mFaceDetectSupported = true;
                int modeSum = 0;
                int[] var5 = availableFDModes;
                int var6 = availableFDModes.length;

                for(int var7 = 0; var7 < var6; ++var7) {
                    int fdMode = var5[var7];
                    modeSum += fdMode;
                }

                if (modeSum % 2 != 0) {
                    this.mFaceDetectMode = 1;
                } else {
                    this.mFaceDetectMode = 2;
                }
            }

            Log.i("CAMERA2", "allocate() face detection: " + this.mFaceDetectMode + " " + maxFDCount + " " + this.mFaceDetectSupported);
            this.mManager.registerAvailabilityCallback(this.mAvailabilityCallback, this.mMainHandler);
            return 0;
        }
    }

    public int setCaptureFormat(int format) {
        int androidFormat = translateToAndroidFormat(format);
        if (androidFormat != this.mCaptureFormat) {
            Log.e("CAMERA2", "For camera2 api, only YUV_420_888 format are supported");
            return -1;
        } else {
            return 0;
        }
    }

    public int startCapture(int width, int height, int frameRate) {
        Log.d("CAMERA2", "startCapture, w=" + width + ", h=" + height + ", fps=" + frameRate);
        this.mCaptureWidth = width;
        this.mCaptureHeight = height;
        this.mCaptureFps = frameRate;
        synchronized(this.mCameraStateLock) {
            while(this.mCameraState != VideoCaptureCamera2.CameraState.STARTED && this.mCameraState != VideoCaptureCamera2.CameraState.EVICTED && this.mCameraState != VideoCaptureCamera2.CameraState.STOPPED) {
                try {
                    this.mCameraStateLock.wait();
                } catch (InterruptedException var7) {
                    Log.e("CAMERA2", "CaptureStartedEvent: ", var7);
                }
            }

            if (this.mCameraState == VideoCaptureCamera2.CameraState.STARTED) {
                return 0;
            }
        }

        this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.OPENING);
        int res = this.tryOpenCamera();
        if (res != 0) {
            this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STOPPED);
        }

        return res;
    }

    public int stopCapture() {
        synchronized(this.mCameraStateLock) {
            while(this.mCameraState != VideoCaptureCamera2.CameraState.STARTED && this.mCameraState != VideoCaptureCamera2.CameraState.EVICTED && this.mCameraState != VideoCaptureCamera2.CameraState.STOPPED) {
                try {
                    this.mCameraStateLock.wait();
                } catch (InterruptedException var4) {
                    Log.e("CAMERA2", "CaptureStartedEvent: ", var4);
                }
            }

            if (this.mCameraState == VideoCaptureCamera2.CameraState.EVICTED) {
                this.mCameraState = VideoCaptureCamera2.CameraState.STOPPED;
            }

            if (this.mCameraState == VideoCaptureCamera2.CameraState.STOPPED) {
                return 0;
            } else {
                this.doStopCapture();
                this.mCameraState = VideoCaptureCamera2.CameraState.STOPPED;
                this.mCameraStateLock.notifyAll();
                return 0;
            }
        }
    }

    public boolean isTorchSupported() {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return false;
        } else {
            Boolean available = (Boolean)cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            boolean isFlashSupported = available == null ? false : available;
            return isFlashSupported;
        }
    }

    public boolean isFocusSupported() {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return false;
        } else {
            int[] availableModes = (int[])cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
            if (availableModes != null) {
                for(int i = 0; i < availableModes.length; ++i) {
                    if (1 == i) {
                        return true;
                    }
                }
            }

            return false;
        }
    }

    public boolean isExposureSupported() {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return false;
        } else {
            int[] availableModes = (int[])cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
            if (availableModes != null) {
                for(int i = 0; i < availableModes.length; ++i) {
                    Log.d("CAMERA2", "isExposureSupported AE mode = " + availableModes[i]);
                    if (1 == i) {
                        return true;
                    }
                }
            }

            return false;
        }
    }

    public boolean isZoomSupported() {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return false;
        } else {
            float maxZoom = (Float)cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
            return maxZoom > 1.0F;
        }
    }

    public boolean isAutoFaceFocusSupported() {
        if (!this.isFocusSupported()) {
            return false;
        } else {
            CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
            if (cameraCharacteristics == null) {
                Log.w("CAMERA2", "warning cameraCharacteristics is null");
                return false;
            } else {
                Integer maxFaceCount = (Integer)cameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT);
                return maxFaceCount > 0;
            }
        }
    }

    public int setZoom(float zoomValue) {
        Log.d("zoom", "setCameraZoom api2 called zoomValue =" + zoomValue);
        if (this.mPreviewBuilder == null) {
            Log.d("CAMERA2", "setZoom mPreviewBuilder is null");
            return -1;
        } else {
            if (this.mSensorRect == null) {
                CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
                if (cameraCharacteristics == null) {
                    Log.w("CAMERA2", "warning cameraCharacteristics is null");
                    return -1;
                }

                this.mSensorRect = (Rect)cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
                this.mMaxZoom = (Float)cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
            }

            if (Math.abs(this.mMaxZoom - 1.0F) < 0.001F) {
                Log.w("CAMERA2", "Camera " + this.mId + " does not support camera zoom");
                return -1;
            } else {
                this.mCurZoomRatio = zoomValue;
                boolean needZoom = this.mCurZoomRatio > 1.0F && this.mCurZoomRatio <= this.mMaxZoom && this.mCurZoomRatio != this.mLastZoomRatio;
                if (!needZoom) {
                    return -2;
                } else {
                    Rect zoomRect = this.cropRegionForZoom(this.mCurZoomRatio);
                    this.mPreviewBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);
                    this.mLastZoomRatio = this.mCurZoomRatio;
                    if (this.mPreviewThread != null) {
                        Handler backgroundHandler = new Handler(this.mPreviewThread.getLooper());
                        if (this.mCaptureSession != null) {
                            try {
                                this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), this.mCaptureCallback, backgroundHandler);
                            } catch (CameraAccessException var6) {
                                var6.printStackTrace();
                                return -3;
                            } catch (IllegalStateException var7) {
                                var7.printStackTrace();
                                return -4;
                            }
                        }
                    }

                    return 0;
                }
            }
        }
    }

    public float getMaxZoom() {
        if (this.mMaxZoom <= 0.0F) {
            CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
            if (cameraCharacteristics == null) {
                Log.w("CAMERA2", "warning cameraCharacteristics is null");
                return -1.0F;
            }

            this.mMaxZoom = (Float)cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
        }

        return this.mMaxZoom;
    }

    public int setTorchMode(boolean isTorchOn) {
        Log.d("flash", "setFlashMode isTorchOn " + isTorchOn);
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return -1;
        } else {
            Boolean available = (Boolean)cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            boolean isFlashSupported = available == null ? false : available;
            if (isFlashSupported) {
                if (this.mPreviewThread != null && this.mPreviewBuilder != null) {
                    Handler backgroundHandler = new Handler(this.mPreviewThread.getLooper());
                    if (isTorchOn) {
                        this.mPreviewBuilder.set(CaptureRequest.FLASH_MODE, 2);
                    } else {
                        this.mPreviewBuilder.set(CaptureRequest.FLASH_MODE, 0);
                    }

                    if (this.mCaptureSession != null) {
                        try {
                            this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), null, backgroundHandler);
                            return 0;
                        } catch (CameraAccessException var7) {
                            var7.printStackTrace();
                        } catch (IllegalStateException var8) {
                            var8.printStackTrace();
                        }
                    }
                }
            } else {
                Log.w("CAMERA2", "flash is not supported");
            }

            return -1;
        }
    }

    public int setFocus(float valX, float valY, boolean inPreview) {
        if (valX >= 0.0F && valX <= 1.0F && valY >= 0.0F && valY <= 1.0F) {
            if (this.mPreviewBuilder == null) {
                Log.d("CAMERA2", "setFocus mPreviewBuilder is null");
                return -1;
            } else {
                double x = (double)valX;
                double y = (double)valY;
                Rect cropRegion = (Rect)this.mPreviewBuilder.get(CaptureRequest.SCALER_CROP_REGION);
                if (cropRegion == null) {
                    return -1;
                } else {
                    int cropWidth = cropRegion.width();
                    int cropHeight = cropRegion.height();
                    Log.d("test", "crop width = " + cropWidth + " crop height = " + cropHeight + " capture width = " + this.mCaptureWidth + " capture height = " + this.mCaptureHeight);
                    int tempX = 0;
                    int tempY = 0;
                    int tempCropW;
                    if (cropWidth * this.mCaptureHeight > cropHeight * this.mCaptureWidth) {
                        tempCropW = cropHeight * this.mCaptureWidth / this.mCaptureHeight;
                        tempX = (int)((double)((float)(cropWidth - tempCropW) / 2.0F) + x * (double)tempCropW);
                        tempY = (int)(y * (double)cropHeight);
                    } else {
                        tempCropW = cropWidth * this.mCaptureHeight / this.mCaptureWidth;
                        tempX = (int)(x * (double)cropWidth);
                        tempY = (int)((double)((float)(cropHeight - tempCropW) / 2.0F) + y * (double)tempCropW);
                    }

                    double tapAreaRatio = 0.1D;
                    Rect rect = new Rect();
                    rect.left = clamp((int)((double)tempX - tapAreaRatio / 2.0D * (double)cropWidth), 0, cropWidth);
                    rect.right = clamp((int)((double)tempX + tapAreaRatio / 2.0D * (double)cropWidth), 0, cropWidth);
                    rect.top = clamp((int)((double)tempY - tapAreaRatio / 2.0D * (double)cropHeight), 0, cropHeight);
                    rect.bottom = clamp((int)((double)tempY + tapAreaRatio / 2.0D * (double)cropHeight), 0, cropHeight);
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{new MeteringRectangle(rect, 1000)});
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{new MeteringRectangle(rect, 1000)});
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, 1);
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 1);
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1);
                    if (this.mPreviewThread != null) {
                        Handler backgroundHandler = new Handler(this.mPreviewThread.getLooper());
                        if (this.mCaptureSession != null) {
                            try {
                                this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), this.mAfCaptureCallback, backgroundHandler);
                            } catch (CameraAccessException var18) {
                                var18.printStackTrace();
                                return -1;
                            } catch (IllegalStateException var19) {
                                var19.printStackTrace();
                                return -1;
                            }
                        }

                        if (this.mNativeVideoCaptureDeviceAndroid != 0L) {
                            this.NotifyCameraFocusAreaChanged(valX, valY, 0.0F, 0.0F, this.mNativeVideoCaptureDeviceAndroid);
                        }
                    }

                    return 0;
                }
            }
        } else {
            Log.e("CAMERA2", "set focus unreasonable inputs");
            return -1;
        }
    }

    public int setExposure(float valX, float valY, boolean inPreview) {
        Log.d("CAMERA2", "setExposure called camera api2");
        if (valX >= 0.0F && valX <= 1.0F && valY >= 0.0F && valY <= 1.0F) {
            if (this.mPreviewBuilder == null) {
                Log.d("CAMERA2", "setExposure mPreviewBuilder is null");
                return -1;
            } else {
                double x = (double)valX;
                double y = (double)valY;
                Rect cropRegion = (Rect)this.mPreviewBuilder.get(CaptureRequest.SCALER_CROP_REGION);
                if (cropRegion == null) {
                    return -1;
                } else {
                    int cropWidth = cropRegion.width();
                    int cropHeight = cropRegion.height();
                    Log.d("CAMERA2", "crop width = " + cropWidth + " crop height = " + cropHeight + " capture width = " + this.mCaptureWidth + " capture height = " + this.mCaptureHeight);
                    int tempX = 0;
                    int tempY = 0;
                    int tempCropW;
                    if (cropWidth * this.mCaptureHeight > cropHeight * this.mCaptureWidth) {
                        tempCropW = cropHeight * this.mCaptureWidth / this.mCaptureHeight;
                        tempX = (int)((double)((float)(cropWidth - tempCropW) / 2.0F) + x * (double)tempCropW);
                        tempY = (int)(y * (double)cropHeight);
                    } else {
                        tempCropW = cropWidth * this.mCaptureHeight / this.mCaptureWidth;
                        tempX = (int)(x * (double)cropWidth);
                        tempY = (int)((double)((float)(cropHeight - tempCropW) / 2.0F) + y * (double)tempCropW);
                    }

                    double tapAreaRatio = 0.1D;
                    Rect rect = new Rect();
                    rect.left = clamp((int)((double)tempX - tapAreaRatio / 2.0D * (double)cropWidth), 0, cropWidth);
                    rect.right = clamp((int)((double)tempX + tapAreaRatio / 2.0D * (double)cropWidth), 0, cropWidth);
                    rect.top = clamp((int)((double)tempY - tapAreaRatio / 2.0D * (double)cropHeight), 0, cropHeight);
                    rect.bottom = clamp((int)((double)tempY + tapAreaRatio / 2.0D * (double)cropHeight), 0, cropHeight);
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{new MeteringRectangle(rect, 1000)});
                    this.mPreviewBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1);
                    if (this.mCaptureSession != null) {
                        try {
                            this.mCaptureSession.setRepeatingRequest(this.mPreviewBuilder.build(), null, (Handler)null);
                        } catch (CameraAccessException var17) {
                            var17.printStackTrace();
                            return -1;
                        } catch (IllegalStateException var18) {
                            var18.printStackTrace();
                            return -1;
                        }
                    }

                    if (this.mNativeVideoCaptureDeviceAndroid != 0L) {
                        this.NotifyCameraExposureAreaChanged(valX, valY, 0.0F, 0.0F, this.mNativeVideoCaptureDeviceAndroid);
                    }

                    return 0;
                }
            }
        } else {
            Log.e("CAMERA2", "set exposure unreasonable inputs");
            return -1;
        }
    }

    public int setAutoFaceFocus(boolean enable) {
        this.mIsAutoFaceFocusEnabled = enable;
        return 0;
    }

    public void deallocate() {
        if (this.mManager != null) {
            this.mManager.unregisterAvailabilityCallback(this.mAvailabilityCallback);
        }

    }

    private void setFaceDetect(CaptureRequest.Builder requestBuilder, int faceDetectMode) {
        if (this.mFaceDetectSupported) {
            requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, faceDetectMode);
        }

    }

    public static int createCapabilities(int id, Context context) {
        String cap = null;
        CameraCharacteristics ch = getCameraCharacteristics(context, id);
        if (ch == null) {
            return -1;
        } else {
            StreamConfigurationMap map = (StreamConfigurationMap)ch.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            if (map == null) {
                Log.e("CAMERA2", "Failed to create capabilities");
                return -1;
            } else {
                try {
                    Log.i("CAMERA2", "dump configuration map:" + map.toString());
                } catch (Exception var13) {
                    var13.printStackTrace();
                }

                List<Size> sizes = new ArrayList(Arrays.asList(map.getOutputSizes(35)));
                if ("SM-G9300".equals(Build.MODEL)) {
                    List<Size> preferSizes = new ArrayList();

                    for(int i = 0; i < sizes.size(); ++i) {
                        if (((Size)sizes.get(i)).getHeight() >= 720) {
                            preferSizes.add(sizes.get(i));
                        }
                    }

                    sizes = preferSizes;
                }

                String cap_id = "\"id\":" + id + ",";
                String cap_res_header = "\"resolution\":";
                String cap_res_value = "";

                String ss;
                for(int i = 0; i < sizes.size(); ++i) {
                    int width = ((Size)sizes.get(i)).getWidth();
                    int height = ((Size)sizes.get(i)).getHeight();
                    if (width >= 240 && height >= 240 && (width >= 320 || height >= 320)) {
                        ss = "{\"w\":" + width + ",\"h\":" + height + "}";
                        if (!cap_res_value.isEmpty()) {
                            cap_res_value = cap_res_value + "," + ss;
                        } else {
                            cap_res_value = ss;
                        }
                    }
                }

                String cap_fmt_header = "\"format\":";
                String cap_fmt_value = "" + translateToEngineFormat(35);
                String cap_fps_header = "\"fps\":";
                ss = "30";
                cap = "{" + cap_id + cap_res_header + "[" + cap_res_value + "]," + cap_fmt_header + "[" + cap_fmt_value + "]," + cap_fps_header + "[" + ss + "]}";
                cacheCapability(id, context, cap, getCaptureName());
                return 0;
            }
        }
    }

    private Rect cropRegionForZoom(float ratio) {
        int xCenter = this.mSensorRect.width() / 2;
        int yCenter = this.mSensorRect.height() / 2;
        int xDelta = (int)(0.5F * (float)this.mSensorRect.width() / ratio);
        int yDelta = (int)(0.5F * (float)this.mSensorRect.height() / ratio);
        return new Rect(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
    }

    private static int clamp(int val, int min, int max) {
        return Math.max(min, Math.min(max, val));
    }

    private boolean isMeteringAreaAFSupported() {
        CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(this.mContext, this.mId);
        if (cameraCharacteristics == null) {
            Log.w("CAMERA2", "warning cameraCharacteristics is null");
            return false;
        } else {
            return (Integer)cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF) >= 1;
        }
    }

    private class ImageReaderListener implements ImageReader.OnImageAvailableListener {
        private ImageReaderListener() {
        }

        public void onImageAvailable(ImageReader reader) {
            Image image = null;

            try {
                synchronized(VideoCaptureCamera2.this.mCameraStateLock) {
                    if (VideoCaptureCamera2.this.mCameraState == VideoCaptureCamera2.CameraState.STARTED && reader != null) {
                        image = reader.acquireLatestImage();
                        if (image != null) {
                            if (image.getFormat() == 35 && image.getPlanes().length == 3) {
                                if (reader.getWidth() == image.getWidth() && reader.getHeight() == image.getHeight()) {
                                    VideoCaptureCamera2.readImageIntoBuffer(image, VideoCaptureCamera2.this.mCaptureData);
                                    if (mNativeVideoCaptureDeviceAndroid != 0L) {
                                        ProvideCameraFrame(VideoCaptureCamera2.this.mCaptureData, VideoCaptureCamera2.this.mExpectedFrameSize, VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid);
                                    } else {
                                        Log.w("CAMERA2", "warning mNativeVideoCaptureDeviceAndroid = 0, error");
                                    }

                                } else {
                                    throw new IllegalStateException("ImageReader size " + reader.getWidth() + "x" + reader.getHeight() + " did not match Image size: " + image.getWidth() + "x" + image.getHeight());
                                }
                            } else {
                                Log.e("CAMERA2", "Unexpected image format: " + image.getFormat() + "or #planes:" + image.getPlanes().length);
                            }
                        }
                    }
                }
            } catch (IllegalStateException var10) {
                Log.e("CAMERA2", "acquireLastest Image():", var10);
            } finally {
                if (image != null) {
                    image.close();
                }

            }
        }
    }

    private class CaptureSessionListener extends CameraCaptureSession.StateCallback {
        private CaptureSessionListener() {
        }

        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
            VideoCaptureCamera2.this.mCaptureSession = cameraCaptureSession;
            if (VideoCaptureCamera2.this.createCaptureRequest() != 0) {
                VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STOPPED);
                if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                    VideoCaptureCamera2.this.onCameraError(VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid, "Fail to setup capture session");
                }
            } else {
                VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STARTED);
            }

        }

        public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
            Log.e("CAMERA2", "onConfigureFailed");
            if (VideoCaptureCamera2.this.mCameraState != VideoCaptureCamera2.CameraState.EVICTED) {
                VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STOPPED);
            }

            if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                VideoCaptureCamera2.this.onCameraError(VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid, "Camera session configuration error");
            }

        }
    }

    private class CrStateListener extends android.hardware.camera2.CameraDevice.StateCallback {
        private CrStateListener() {
        }

        public void onOpened(CameraDevice cameraDevice) {
            VideoCaptureCamera2.this.mCameraDevice = cameraDevice;
            if (VideoCaptureCamera2.this.doStartCapture() < 0) {
                VideoCaptureCamera2.this.doStopCapture();
                if (VideoCaptureCamera2.this.mCameraState != VideoCaptureCamera2.CameraState.EVICTED) {
                    VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STOPPED);
                }

                Log.e("CAMERA2", "Camera startCapture failed!!");
                if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                    VideoCaptureCamera2.this.onCameraError(VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid, "Error configuring camera");
                }
            }

        }

        public void onDisconnected(CameraDevice cameraDevice) {
            if (VideoCaptureCamera2.this.mCameraState != VideoCaptureCamera2.CameraState.STOPPED) {
                Log.w("CAMERA2", "camera client is evicted by other application");
                if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                    VideoCaptureCamera2.this.onCameraError(VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid, "Camera device evicted by other application");
                }

                Log.i("CAMERA2", "Camera device enter state: EVICTED");
                if (VideoCaptureCamera2.this.mCameraDevice != null) {
                    VideoCaptureCamera2.this.mCameraDevice.close();
                    VideoCaptureCamera2.this.mCameraDevice = null;
                }

                VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.EVICTED);
            }
        }

        public void onError(CameraDevice cameraDevice, int error) {
            if (VideoCaptureCamera2.this.mCameraState != VideoCaptureCamera2.CameraState.EVICTED) {
                if (VideoCaptureCamera2.this.mCameraDevice != null) {
                    VideoCaptureCamera2.this.mCameraDevice.close();
                    VideoCaptureCamera2.this.mCameraDevice = null;
                }

                VideoCaptureCamera2.this.changeCameraStateAndNotify(VideoCaptureCamera2.CameraState.STOPPED);
                Log.e("CAMERA2", "CameraDevice Error :" + Integer.toString(error));
                if (VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid != 0L) {
                    VideoCaptureCamera2.this.onCameraError(VideoCaptureCamera2.this.mNativeVideoCaptureDeviceAndroid, "Camera device error" + Integer.toString(error));
                }

            }
        }
    }

    private static enum CameraState {
        OPENING,
        STARTED,
        EVICTED,
        STOPPED;

        private CameraState() {
        }
    }
}